package cn.xzqwjw.taskmanager.service.impl;

import cn.xzqwjw.taskmanager.common.customEnum.ResponseCodeEnum;
import cn.xzqwjw.taskmanager.common.exception.CustomException;
import cn.xzqwjw.taskmanager.domain.pojo.UlFile;
import cn.xzqwjw.taskmanager.domain.vo.ResponseVo;
import cn.xzqwjw.taskmanager.domain.vo.UploadConfigVo;
import cn.xzqwjw.taskmanager.repository.UlFileMapper;
import cn.xzqwjw.taskmanager.service.SysConfigService;
import cn.xzqwjw.taskmanager.service.UlFileService;
import cn.xzqwjw.taskmanager.utils.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @author wjw
 */
@Service("UlFileService")
@Transactional(rollbackFor = Exception.class)
public class UlFileServiceImpl extends ServiceImpl<UlFileMapper, UlFile> implements UlFileService {

  private final UlFileMapper ulFileMapper;
  private final SysConfigService configService;

  @Autowired
  public UlFileServiceImpl(UlFileMapper ulFileMapper, SysConfigService configService) {
    this.ulFileMapper = ulFileMapper;
    this.configService = configService;
  }

  // ========== 文件上传下载功能 ==========

  /**
   * 上传单个文件
   */
  @Override
  public String upload2Local(int idAdmin, int idTask, String fileNameShow, MultipartFile file) {
    UploadConfigVo configVo = getUploadConfig();
    configVo.setFileNameShow(fileNameShow);
    UldlUtil.upload2Local(file, configVo);
    // 根据上传结果设置返回的map
    Map<String, String> resultMap = setUploadResultMap(idAdmin, idTask, configVo);
    String message = resultMap.get("Message");
    if (ResponseCodeEnum.SUCCESS.getMessage().equals(message)) {
      return JsonUtil.obj2Json(ResponseVo.success(resultMap));
    } else {
      return JsonUtil.obj2Json(ResponseVo.error(message));
    }
  }

  @Override
  public String upload2LocalMulti(int idAdmin, int idTask, MultipartFile[] files) {
    // 上传文件到本地服务器上
    List<Map<String, String>> resultList = uploadMultiFile(idAdmin, idTask, files);
    return JsonUtil.obj2Json(ResponseVo.success(resultList));
  }

  /**
   * URI跟URL差不多，不过URI一定能确定资源的位置，URL不一定
   * 比如 "h ttp://localhost:8080/hello-world.jpg" 和 "/helloWord.jpg" 都是URL
   * 但是前者是URI，后者不是URI
   * Resource将文件抽象为资源，并且提供了很多非常好用的方法
   * 如获取文件，获取其URL和绝对路径等
   */
  @Override
  public void downloadAsStream(HttpServletResponse response, String uuid) {
    CommonUtil.checkNull(response, uuid);
    String entireFileName = getEntireFileNameByUuid(uuid, false);
    UldlUtil.downloadAsStream(response, entireFileName);
  }

  /**
   * 通过 ResponseEntity 实现封装 ResponseEntity，将文件流写入body中
   */
  @Override
  public ResponseEntity<InputStreamResource> downloadAsResponseEntity(String uuid) {
    CommonUtil.checkNull(uuid);
    String entireFileName = getEntireFileNameByUuid(uuid, false);
    return UldlUtil.downloadAsResponseEntity(entireFileName);
  }

  @Override
  public byte[] showImgAsStream(HttpServletResponse response, String uuid, Boolean flagThumb) {
    CommonUtil.checkNull(response, uuid);
    String entireFileName = getEntireFileNameByUuid(uuid, flagThumb);
    return UldlUtil.showImgAsStream(response, entireFileName);
  }

  // ==================== 文件管理功能 ====================

  @Override
  public String delById(int id) {
    // 1、根据id得到文件存储信息
    UlFile fileInfo = getById(id);
    // 2、删除物理文件
    delRhysicsFile(fileInfo);
    // 3、删除数据库中的上传记录
    if (removeById(id)) {
      return JsonUtil.obj2Json(ResponseVo.success(ResponseCodeEnum.OK_DELETED));
    } else {
      throw new CustomException(ResponseCodeEnum.ERROR_DELETE);
    }
  }

  @Override
  public String delByUuid(String uuid) {
    CommonUtil.checkNull(uuid);
    // 1、根据id得到文件存储信息
    UlFile fileInfo = queryInfoByUuid(uuid);
    // 2、删除物理文件
    delRhysicsFile(fileInfo);
    // 3、删除数据库中的上传记录
    QueryWrapper<UlFile> wrapper = new QueryWrapper<>();
    wrapper.eq("uuid", uuid);
    if (remove(wrapper)) {
      return JsonUtil.obj2Json(ResponseVo.success(ResponseCodeEnum.OK_DELETED));
    } else {
      throw new CustomException(ResponseCodeEnum.ERROR_DELETE);
    }
  }

  @Override
  public String modifyAll(UlFile file) {
    CommonUtil.checkNull(file);
    if (updateById(file)) {
      return JsonUtil.obj2Json(ResponseVo.success(ResponseCodeEnum.OK_UPDATED));
    } else {
      throw new CustomException(ResponseCodeEnum.ERROR_UPDATE);
    }
  }

  /**
   * 修改上传的文件的 tag、remark
   */
  @Override
  public String patchByParams(int id, String paramsJson) {
    CommonUtil.checkNull(id, paramsJson);
    Map<?, ?> map = JsonUtil.json2Obj(paramsJson, Map.class);
    String tag = "";
    String remark = "";
    String fieldTag = "Tag";
    String fieldRemark = "Remark";
    UpdateWrapper<UlFile> wrapper = new UpdateWrapper<>();
    if (Objects.nonNull(map.get(fieldTag))) {
      tag = map.get(fieldTag).toString();
    }
    if (StringUtils.hasLength(tag)) {
      wrapper.set("tag", tag);
    }
    if (Objects.nonNull(map.get(fieldRemark))) {
      remark = map.get(fieldRemark).toString();
    }
    if (StringUtils.hasLength(remark)) {
      wrapper.set("remark", remark);
    }
    wrapper.eq("id", id);
    if (update(wrapper)) {
      return JsonUtil.obj2Json(ResponseVo.success(ResponseCodeEnum.OK_UPDATED));
    } else {
      throw new CustomException(ResponseCodeEnum.ERROR_PATCH);
    }
  }

  @Override
  public String queryById(int id) {
    CommonUtil.checkId(id);
    return JsonUtil.obj2Json(ResponseVo.success(getById(id)));
  }

  @Override
  public String queryByUuid(String uuid) {
    return JsonUtil.obj2Json(ResponseVo.success(queryInfoByUuid(uuid)));
  }

  @Override
  public String listPage(int pageCurrent, int pageSize) {
    Page<Map<String, Object>> page = CommonUtil.getPage(pageCurrent, pageSize);
    List<Map<String, Object>> listData = ulFileMapper.listPage(page).getRecords();
    return ResponseUtil.writePageData(page, listData, pageSize);
  }

  @Override
  public String listAll(int idTask) {
    QueryWrapper<UlFile> wrapper = new QueryWrapper<>();
    wrapper.eq("id_task", idTask);
    return JsonUtil.obj2Json(ResponseVo.success(list(wrapper)));
  }

  // ==================== 用到的私有函数 ====================

  /**
   * 将上传文件信息保存到数据库中，返回文件的uuid
   */
  private String save2Db(int idAdmin, int idTask, UploadConfigVo configVo) {
    UlFile ulFile = new UlFile();
    ulFile.setUuid(MyStringUtil.getUuid());
    ulFile.setIdAdmin(idAdmin);
    ulFile.setTag("");
    ulFile.setDtUpload(LocalDateTime.now());
    ulFile.setPathEntire(configVo.getPathEntire());
    ulFile.setPathRelative(configVo.getPathRelative());
    ulFile.setFileName(configVo.getServerFileName());
    ulFile.setExtName(configVo.getExtName());
    ulFile.setFileHash("");
    ulFile.setFileSize(configVo.getFileSize());
    ulFile.setMimeType(configVo.getMimeType());
    ulFile.setRemark("");
    ulFile.setFlagDeleted(false);
    ulFile.setIdTask(idTask);
    ulFile.setFileNameClient(configVo.getClientFileName());
    ulFile.setFileNameShow(configVo.getFileNameShow());
    if (save(ulFile)) {
      return ulFile.getUuid();
    } else {
      return "";
    }
  }

  /**
   * 上传多个文件到服务器
   */
  private List<Map<String, String>> uploadMultiFile(int idAdmin, int idTask, MultipartFile[] uploadFiles) {
    UploadConfigVo configVo;
    List<Map<String, String>> resultList = new ArrayList<>();
    // 循环获取数组中的文件
    for (MultipartFile file : uploadFiles) {
      configVo = getUploadConfig();
      UldlUtil.upload2Local(file, configVo);
      resultList.add(setUploadResultMap(idAdmin, idTask, configVo));
    }
    return resultList;
  }

  private void delRhysicsFile(UlFile file) {
    if (Objects.isNull(file)) {
      return;
    }
    // 如果存在缩略图，则删除缩略图
    String fileString = file.getPathEntire() + file.getFileName() + "_nb.jpg";
    if (new File(fileString).exists()) {
      FileUtil.delRhysicsFile(fileString);
    }
    // 如果存在水印图，则删除水印图
    fileString = file.getPathEntire() + file.getFileName() + "_wm.jpg";
    if (new File(fileString).exists()) {
      FileUtil.delRhysicsFile(fileString);
    }
    String entireName = file.getPathEntire() + file.getFileName() + file.getExtName();
    FileUtil.delRhysicsFile(entireName);
  }

  /**
   * 将上传结果添加到数据库中，并设置返回给前端的Map值
   */
  private Map<String, String> setUploadResultMap(int idAdmin, int idTask, UploadConfigVo configVo) {
    Map<String, String> resultMap = new HashMap<>(16);
    // 上传失败，返回错误信息
    if (!configVo.getOpFlag()) {
      resultMap.put("Message", configVo.getOpMsg().getMessage());
      return resultMap;
    }
    // 上传成功，数据库中添加一条上传记录
    String uuid = save2Db(idAdmin, idTask, configVo);
    // 数据库添加不成功，返回错误信息
    if (!StringUtils.hasLength(uuid)) {
      resultMap.put("Message", ResponseCodeEnum.ERROR_INSERT.getMessage());
      return resultMap;
    }
    // 数据库添加成功，返回正确信息
    // String urlRoot = "/upload-file/";
    // 得到 "image\2022\01\02\" 这样的带服务器操作系统规定的硬盘路径分隔符的路径字符串
    String pathRelative = configVo.getPathRelative();
    pathRelative = "/" + pathRelative.replace(File.separator, "/");
    // 构建上传文件的url
    String url = pathRelative + configVo.getServerFileName() + configVo.getExtName();
    // 构建缩略图和水印图的url
    String pathEntire = configVo.getPathEntire();
    String fileNameServer = configVo.getServerFileName();
    String entireFileNameThumb = pathEntire + fileNameServer + "_tn.jpg";
    String urlThumb = "";
    File file = new File(entireFileNameThumb);
    if (file.exists()) {
      urlThumb = pathRelative + configVo.getServerFileName() + "_tn.jpg";
    }
    // 构建缩略图和水印图的url
    String entireFileNameWm = pathEntire + fileNameServer + "_wm.jpg";
    String urlWm = "";
    file = new File(entireFileNameWm);
    if (file.exists()) {
      urlWm = pathRelative + configVo.getServerFileName() + "_wm.jpg";
    }
    resultMap.put("Message", "OK");
    resultMap.put("uuid", uuid);
    resultMap.put("FileNameClient", configVo.getClientFileName());
    resultMap.put("FileNameShow", configVo.getFileNameShow());
    resultMap.put("ExtName", configVo.getExtName());
    resultMap.put("Url", url);
    resultMap.put("UrlThumb", urlThumb);
    resultMap.put("UrlWm", urlWm);
    resultMap.put("MimeType", configVo.getMimeType());
    return resultMap;
  }

  /**
   * 根据uuid得到上传文件的信息
   */
  private UlFile queryInfoByUuid(String uuid) {
    return ulFileMapper.getDetailByUuid(uuid);
  }

  /**
   * 根据uuid获取文件的完整文件名（硬盘绝对路径+文件名+扩展名）
   *
   * @param uuid      要获取文件的uuid
   * @param flagThumb true 输出缩略图的uri，如果不存在则输出原图 false 输出原图的uri
   */
  private String getEntireFileNameByUuid(String uuid, Boolean flagThumb) {
    UlFile file = queryInfoByUuid(uuid);
    if (Objects.isNull(file)) {
      return "";
    }
    String entireFileName = file.getPathEntire() + file.getFileName() + file.getExtName();
    String entireThumb = file.getPathEntire() + file.getFileName() + "_tn.jpg";
    // String entireWm = file.getPathEntire() + file.getFileName() + "_wm.jpg";
    if (flagThumb) {
      // 输出缩略图，要先验证下缩略图是否存在，如果不存在则输出原图
      if (new File(entireThumb).exists()) {
        return entireThumb;
      } else {
        return entireFileName;
      }
    } else {
      // 不输出缩略图，以原图方式输出
      return entireFileName;
    }
  }

  /**
   * 从数据库获取上传文件的配置
   */
  private UploadConfigVo getUploadConfig() {
    return configService.getUploadConfigDefault();
  }

}
